/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2002, GeoVISTA Center
 * Licensed under Simplified BSD License
 * This library is developed and distributed for free usage,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * You can redistribute and/or modify it under the terms of the
 * Simplified BSD License
 *
 *
 *
 *
 *
 *
*
* A GeoMap that not have visual classifiers but come with toolbar and do not depend on the built-in classifier in MapCanvas
* @Original Author: jin Chen
* @date: Dec 8, 2004$
* @version: 1.0
*/
package edu.psu.geovista.app.gmap;

import edu.psu.geovista.app.gmap.utils.MapConfigure;
import edu.psu.geovista.app.map.MapCanvas;
import edu.psu.geovista.app.visualclassifier.classify.v2.ESVisualClassifier;
import edu.psu.geovista.app.visualclassifier.classify.v2.VisualClassifierClient;
import edu.psu.geovista.app.visualclassifier.classify.v2.agent.ClassificationInterestor;
import edu.psu.geovista.app.visualclassifier.classify.v2.agent.ClassifyAgent;
import edu.psu.geovista.app.visualclassifier.classify.v2.agent.MapClassifyAgent;
import edu.psu.geovista.app.visualclassifier.classify.v2.gui.LegendControl;
import edu.psu.geovista.app.visualclassifier.classify.v2.gui.LegendControl2;
import edu.psu.geovista.classification.ClassificationUtils;
import edu.psu.geovista.classification.GroupClassificationResult;
import edu.psu.geovista.common.awt.event.Coordinator;
import edu.psu.geovista.common.awt.event.dataloading.AppDataEvent;
import edu.psu.geovista.data.geog.utils.AttributesNameIndexs;
import edu.psu.geovista.data.model.AppDataModel;
import edu.psu.geovista.data.model.DefaultAppDataModel;
import edu.psu.geovista.symbolization.event.ColorClassifierListener;
import edu.psu.geovista.ui.cursor.GeoCursors;
import edu.psu.geovista.ui.event.*;
import epoviz.trex.common.swing.frame.AppComponent;
import epoviz.trex.common.swing.frame.ScreenshotComponent;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.net.URL;
import java.util.Arrays;

public class SimpleGeoMap extends JPanel
        implements AppComponent, ScreenshotComponent,VisualClassifierClient,
        IndicationListener,DataSetListener,SelectionListener,
        ConditioningListener,SpatialExtentListener,ColorArrayListener
        ,ClassificationResultListener,GroupClassifyListener,ClassificationInterestor{
    protected MapCanvas mapCan;
    protected JToolBar toolbar;
    protected GeoCursors cursors;
    //protected LegendControl2 legend;
    AttributesNameIndexs attIndex;// 

    ClassifyAgent classifyAgent;// 
    boolean showLegend=true;//interactive legend
    //private boolean showLegendStatic=false; //static legend
    int dataRowcount=0;
    private AppDataModel dataModel;
    private Coordinator coordinator;
    private MapConfigure config;

    public SimpleGeoMap() {
        cursors = new GeoCursors();
        this.setCursor(cursors.getCursor(GeoCursors.CURSOR_ARROW_SELECT));

        this.mapCan=new MapCanvas();
        this.classifyAgent=new MapClassifyAgent(this);//new MyClassifyAgent(this);

        JComponent legend=(JComponent) classifyAgent.getLegendInterative();

        mapCan.setLayout(null);
        mapCan.add(legend);
        this.setLayout(new BorderLayout() );
        this.add(mapCan);

        this.mapCan.setColorColumnX(0);
        this.mapCan.setColorColumnY(0);

        setupToolbar();
        this.add(toolbar,BorderLayout.NORTH );

        mapCan.addMouseListener(new MouseAdapter(){
            public void mouseClicked(MouseEvent e) {
                int btn = e.getButton();
                if(btn==MouseEvent.BUTTON3){
                    showPopupMenu(e.getX(), e.getY() );

                }
            }
        });
        this.coordinator=new Coordinator();
        this.config=new MapConfigure(this);

    }
    public void showLegendInteractive(boolean show){
        JComponent legend=(JComponent) classifyAgent.getLegendInterative();
        if(show){
            this.mapCan.add(legend);

        }
        else{
            this.mapCan.remove(legend);
        }
        this.setShowLegend(show);

        mapCan.revalidate();
        mapCan.repaint();
    }

   

    public boolean isShowLegend() {
        return showLegend;
    }

    private  void setShowLegend(boolean showLegend) {
        this.showLegend = showLegend;
    }


    private void showPopupMenu(int x, int y){
        JPopupMenu pop=new JPopupMenu();

        
        //zoom to full extend
        Action zoomFullAction=new AbstractAction(){
                   public void actionPerformed(ActionEvent e) {
                       SimpleGeoMap.this.mapCan.zoomFullExtent();
                   }
               };

        addAction2Popup("zoom to full",zoomFullAction, pop);

        //coordination
        Action coordAction=new AbstractAction(){
                   public void actionPerformed(ActionEvent e) {
                       SimpleGeoMap.this.coordinator.show() ;
                   }
               };

        addAction2Popup("Set coordination",coordAction, pop);

         //coordination
        Action configAction=new AbstractAction(){
                   public void actionPerformed(ActionEvent e) {
                       SimpleGeoMap.this.config.show() ;
                   }
               };

        addAction2Popup("Set Configuration",configAction, pop);

        pop.show(this, x ,y );


    }

   

    private void addAction2Popup( String actname, Action action, JPopupMenu pop){
               action.putValue(Action.NAME, actname);
               JMenuItem menuItem=new JMenuItem(actname);
               menuItem.setAction(action);
               menuItem.setText(actname);
        pop.add(menuItem);
        pop.add(new JPopupMenu.Separator ());
    }

    /**
     * set the 2 variables that displayed in Map upon indication.
     * It also update selected variable in the 2 comboBox which list all the variables
     * @param xvarname
     * @param yvarname
     */
    public void setVariables(String xvarname,String yvarname){
        int[] indexs = this.classifyAgent.getIndexs(xvarname,yvarname);
        if(indexs.length ==0)return;//initially


        int Xindex =indexs[0]-1;
        int Yindex=indexs[1]-1;
        if(Xindex>=0)
            this.mapCan.setColorColumnX(Xindex);
        //else
        // this.mapCan.setColorColumnX(-1);

        if(Yindex>=0)
            this.mapCan.setColorColumnY(Yindex); // set variables which show up when indication going on

        // this.classifyAgent.setVariables(xvarname,yvarname);
    }
    /*******************************************************************************************************
     *                Event Listeners
     *******************************************************************************************************/
    public void classificationResultChanged(ClassificationResultEvent e) {

       if(coordinator.isClassification())
        classifyAgent.classificationResultChanged(e);

    }
    public void groupClassifyChanged(GroupClassifyEvent e) {
        GroupClassificationResult cr = e.getClassifyResult();
        Color[] dfcolors=null;
        if(this.dataRowcount >0){
            dfcolors=new Color[dataRowcount];
            Arrays.fill(dfcolors, Color.gray);
           
        }
        Color[] datacolors = ClassificationUtils.getDataColor(cr,dfcolors);
         if (this.mapCan == null ||datacolors == null) {
            return;
        }
    this.mapCan.setObservationColors(datacolors);

    }


    public void colorArrayChanged(ColorArrayEvent e) {
        if (this.mapCan == null || e.getColors() == null) {
            return;
        }
         if(coordinator.isColor())
        this.mapCan.setObservationColors(e.getColors());
    }
    public void indicationChanged(IndicationEvent e) {
         if(coordinator.isIndication())
        mapCan.indicationChanged(e);
    }

    public void dataSetChanged(DataSetEvent e) {
        if( e instanceof AppDataEvent){
            AppDataEvent evt=(AppDataEvent) e;
            AppDataModel dataModel = evt.getDataModel();
            try {
                this.setDataModel((AppDataModel) dataModel);
            } catch (Exception e1) {
                //System.out.
                this.setDataSet(e.getDataSet() );
            }
        }
        else
            this.setDataSet(e.getDataSet());
    }

    public void selectionChanged(SelectionEvent e) {
        if(coordinator.isSelection()){
            int[] curSel = mapCan.getSelectedObservationsInt();
            int[] sel = e.getSelection();
            if(!Arrays.equals(sel,curSel) ){
                mapCan.selectionChanged(e);
                //System.out.println(this.getClass().getSimpleName() +".selectionChanged()"+sel.length );
            }
        }
    }
    public void conditioningChanged(ConditioningEvent e) {
        mapCan.setConditionArray(e.getConditioning());
    }
    public void spatialExtentChanged(SpatialExtentEvent e) {
        mapCan.spatialExtentChanged(e);
    }



    /**
     * adds an IndicationListener
     */
    public void addIndicationListener(IndicationListener l) {
        this.mapCan.addIndicationListener(l);
    }

    /**
     * removes an IndicationListener from the component
     */
    public void removeIndicationListener(IndicationListener l) {
        this.mapCan.removeIndicationListener(l);
    }

    /**
     * adds an SelectionListener
     */
    public void addSelectionListener(SelectionListener l) {
        this.mapCan.addSelectionListener(l);

        LegendControl2 legend = (LegendControl2) this.classifyAgent.getLegendInterative();
        DefaultEventSupport eventSupport = legend.getEventSupport();
        eventSupport.addSelectionListener(l);
    }

    /**
     * removes an SelectionListener from the component
     */
    public void removeSelectionListener(SelectionListener l) {
        this.mapCan.removeSelectionListener(l);
    }

    /**
     * adds an SpatialExtentListener
     */
    public void addSpatialExtentListener(SpatialExtentListener l) {
        this.mapCan.addSpatialExtentListener(l);
    }

    /**
     * removes an SpatialExtentListener from the component
     */
    public void removeSpatialExtentListener(SpatialExtentListener l) {
        listenerList.remove(SpatialExtentListener.class, l);
        this.mapCan.removeSpatialExtentListener(l);
    }
    public void addColorClassifierListener(ColorClassifierListener l) {
        /* this.visClassOne.addColorClassifierListener(l);
        this.visClassTwo.addColorClassifierListener(l);*/

    }

    /**
     * removes an ColorClassifierListener from the component
     */
    public void removeColorClassifierListener(ColorClassifierListener l) {
        /*this.visClassOne.removeColorClassifierListener(l);
        this.visClassTwo.removeColorClassifierListener(l);*/
    }
    /*******************************************************************************************************
     *                properties
     *******************************************************************************************************/
    public void setDataSet(Object[] data) {
        DefaultAppDataModel model=new DefaultAppDataModel() ;

        model.feedData(data);
        try {
            Object[] rawdata = model.getGeoVistaRawData();
            this.setDataModel(model);
        } catch (Exception e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
        /*this.mapCan.setDataSet(data);
        mapCan.zoomFullExtent();
        this.mapCan.setColorColumnY(0); // set variables which show up when indication going on
        this.mapCan.setColorColumnX(1); //

        DataSetForApps dataSet = new DataSetForApps();
        dataSet.setDataObject(data);
        this.classifyAgent.setDataSet(dataSet);
        this.toolbar.revalidate();//The comboBox in the toolbar is updated, so need repaint toolbar
        this.toolbar.repaint();*/


    }
     public void setVisualClassifier(ESVisualClassifier vc) {
       //  
       //  
       //  
       //  
        this.classifyAgent.setVisualClassifier(vc);

    }
   /*  public void setAuxiliarySpatialData(AppDataModel datamodel) {
         
     }*/
   public void setAuxiliarySpatialData(Object[] spatialDataIn) {
       this.mapCan.setAuxiliarySpatialData(spatialDataIn);
   }
    public void setDataModel(AppDataModel datamodel) throws Exception {
        if(datamodel.getShpData()==null){
            System.err.println(this.getClass().getName()+" unable to find shp[] data, fails to instantiate a map.");
            return;
        }
        this.dataModel =datamodel;
        dataRowcount=datamodel.getRowCount();
        Object[] rawdata0 = datamodel.getRaw(); // not use getRaw(), when u load data files separatedly, rawdata do not have .shp data
        Object[] rawdata = datamodel.getGeoVistaRawData();
        this.mapCan.setDataSet(rawdata);
        mapCan.zoomFullExtent();
        this.mapCan.setColorColumnY(0); // set variables which show up when indication going on
        this.mapCan.setColorColumnX(1); //

        this.classifyAgent.setDataModel(datamodel);



        this.toolbar.revalidate();//The comboBox in the toolbar is updated, so need repaint toolbar
        this.toolbar.repaint();


    }
   

    /*******************************************************************************************************
     *                support
     *
     *******************************************************************************************************/

    public void setupToolbar() {
        toolbar = new JToolBar();

        //Dimension prefSize = new Dimension(100,10);
        //mapTools.setMinimumSize(prefSize);
        //mapTools.setPreferredSize(prefSize);
        JButton button = null;
        Class cl = this.getClass();
        URL urlGif = null;
        Dimension buttDim = new Dimension(32, 32);

        //first button
        try {
            urlGif = cl.getResource("resources/select24.gif"); //select16.gif
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Enter selection mode");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_SELECT));
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_SELECT);
            }
        });
        toolbar.add(button);

        toolbar.addSeparator();

        //second button
        try {
            urlGif = cl.getResource("resources/ZoomIn24.gif");//ZoomIn16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Enter zoom in mode");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_ZOOM_IN));
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_ZOOM_IN);

                //SimpleGeoMap.this.setCursor(new Cursor(Cursor.HAND_CURSOR));
            }
        });
        toolbar.add(button);

        //third button
        try {
            urlGif = cl.getResource("resources/ZoomOut24.gif");//ZoomOut16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Enter zoom out mode");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_ZOOM_OUT);
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_ZOOM_OUT));
            }
        });
        toolbar.add(button);

        //fourth button
        try {
            urlGif = cl.getResource("resources/Home24.gif");//Home16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Zoom to full extent");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.mapCan.zoomFullExtent();
            }
        });
        toolbar.add(button);

        //fifth button
        try {
            urlGif = cl.getResource("resources/pan24.gif");//pan16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            System.err.println("When load resources/pan24.gif, got "+urlGif );
            ex.printStackTrace();
        }

        button.setToolTipText("Enter pan mode");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_PAN));
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_PAN);
            }
        });
        toolbar.add(button);
        //sixth button
        try {
            urlGif = cl.getResource("resources/excentric24.gif");  //excentric16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Excentric Labels");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_PAN));
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_EXCENTRIC);
            }
        });
        toolbar.add(button);

        //seventh button
        try {
            urlGif = cl.getResource("resources/fisheye24.gif");  //fisheye16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Fisheye Lens");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_PAN));
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_FISHEYE);
            }
        });
        toolbar.add(button);

        //eighth button
        try {
            urlGif = cl.getResource("resources/magnifying24.gif");   //magnifying16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Magnifiying Lens");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                SimpleGeoMap.this.setCursor(cursors.getCursor(
                        GeoCursors.CURSOR_ARROW_PAN));
                SimpleGeoMap.this.mapCan.setMode(MapCanvas.MODE_FISHEYE);  //.MODE_MAGNIFYING not work
            }
        });

       //show legendInteractive
         try {
            urlGif = cl.getResource("resources/ShowVCLegend.gif");  //fisheye16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

        button.setToolTipText("Show Interactive Legend");
        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                boolean show = SimpleGeoMap.this.isShowLegend();
                SimpleGeoMap.this.showLegendInteractive(!show);
            }
        });
        toolbar.add(button);

         //show legendStatic
         try {
            urlGif = cl.getResource("resources/ShowVCLegend.gif");  //fisheye16
            button = new JButton(new ImageIcon(urlGif));
            button.setPreferredSize(buttDim);
        }
        catch (Exception ex) {
            ex.printStackTrace();
        }

       

        //add variable list
        setupVariableList();


    }

    private void setupVariableList(){

        LegendControl legend = this.classifyAgent.getLegendInterative();
        Color xcolor = legend.getXbarColor();
        Color ycolor = legend.getYbarColor();

        JPanel xp=new JPanel();
        JPanel yp=new JPanel();
        JLabel xlb=new JLabel("X:");
        JLabel ylb=new JLabel("Y:");
        JComboBox xcb = getXcb();
        xcb.setForeground(xcolor);  //214,127,21
        JComboBox ycb = getYcb();
        ycb.setForeground(ycolor);


        xp.add(xlb);

        xp.add(xcb);

        yp.add(ylb);

        yp.add(ycb); //getYcb()
        toolbar.setLayout(new FlowLayout(FlowLayout.LEADING));
        toolbar.add(xp);
        toolbar.add(yp);
    }

    /**
     *
     * @param useblur  if true, use blur when seletion
     */
    public void setUseBlur(boolean useblur){
        this.mapCan.setUseBlur(useblur);
    }


    private JComboBox getXcb() {
        return this.classifyAgent.getXcb();
    }



    private JComboBox getYcb() {
        return this.classifyAgent.getYcb();
    }


    public MapCanvas getMapCan() {
        return mapCan;
    }

    /**
     * provide a view for taking a screenshot
     * @return
     */
    public JComponent getScreenshotView(){
        return getMapCan() ;
    }
    public AppDataModel getDataModel() {
        return dataModel;
    }

    public ClassifyAgent getClassifyAgent() {
        return classifyAgent;
    }

   



}
